﻿/**
 * SnakeStruct
 *
 * @author		Allen Chou
 * @version		1.1.0 (last update: Nov 18 2008)
 * @link        http://cjcat.blogspot.com
 * @link		http://cjcat2266.deviantart.com
 */
 
package idv.cjcat.display.dynashape.structs {
	import flash.events.*;
	import flash.errors.IllegalOperationError;
	
	import idv.cjcat.*;
	import idv.cjcat.display.dynashape.*;
	
	public class SnakeStruct {
		
		/**
		 * The radius of the head of the snake structure.
		 */
		public var headRadius:Number = 20;
		/**
		 * The spring factor of the snake structure.
		 */
		public var springFactor:Number = 0.95;
		
		private var _damping:Number = 0.7;
		private var _segments:Array;
		private var _numSegments:uint;
		
		/**
		 * Creates a snake structure.
		 * @param	headRadius   The head radius of the snake structure.
		 * @param	numSegments  The number of segments of the snake structure.
		 */
		public function SnakeStruct(headRadius:Number = 20, numSegments:uint = 7) {
			this.headRadius = CJMath.clamp(headRadius, 0, Number.MAX_VALUE);
			this.numSegments = numSegments;
		}
		
		/**
		 * @return Points of the snake structure. This is meant to be passed to <code>DynaGraphics.lineThrough()</code> or <code>DynaGraphics.curveThrough()</code>.
		 */
		public function getPoints():Array {
			var temp:Array = new Array();
			for (i = 0; i < _numSegments - 1; i++) temp.push(_segments[i].right);
			temp.push(_segments[_numSegments - 1]);
			for (i = _numSegments - 2; i >= 0; i--) temp.push(_segments[i].left);
			
			return temp;
		}
		
		/**
		 * @return The first half of points series of a snake structure. This is meant to be passed to <code>DynaGraphics.lineThrough()</code> or <code>DynaGraphics.curveThrough()</code>.
		 */
		public function getFirstHalf():Array {
			var temp:Array = new Array();
			for (i = 0; i < _numSegments - 1; i++) temp.push(_segments[i].right);
			temp.push(_segments[_numSegments - 1]);
			return temp;
		}
		
		/**
		 * @return The second half of points series of a snake structure. This is meant to be passed to <code>DynaGraphics.lineThrough()</code> or <code>DynaGraphics.curveThrough()</code>.
		 */
		public function getSecondHalf():Array {
			var temp:Array = new Array();
			temp.push(_segments[_numSegments - 1]);
			for (i = _numSegments - 2; i >= 0; i--) temp.push(_segments[i].left);
			return temp;
		}
		
		/**
		 * @return The head of the snake structure.
		 */
		public function getHead():Object { return _segments[0]; }
		
		private var i:int;
		private var seg:Object;
		private var radius:Number;
		private var length:Number;
		private var unitX:Number;
		private var unitY:Number;
		/**
		 * Steps through a unit of time interval of spring motion simulation.
		 * @param	e
		 */
		public function step(e:Event = null):void {
			//process snake head
			seg = _segments[0];
			seg.vx = seg.x - seg.ox;
			seg.vy = seg.y - seg.oy;
			seg.ox = seg.x;
			seg.oy = seg.y;
			
			//process normal segments;
			for (i = 1; i < _numSegments; i++) {
				seg = _segments[i];
				seg.vx += springFactor * (_segments[i - 1].x - seg.x);
				seg.vy += springFactor * (_segments[i - 1].y - seg.y);
				seg.vx *= (1 - damping);
				seg.vy *= (1 - damping);
			}
			
			//process left & right points
			seg = _segments[0];
			seg.left.x = seg.x;
			seg.left.y = seg.y;
			seg.right.x = seg.x;
			seg.right.y = seg.y;
			for (i = 1; i < _numSegments - 1; i++) {
				seg = _segments[i];
				seg.x += seg.vx;
				seg.y += seg.vy;
				
				radius = headRadius / _numSegments * (_numSegments - i);
				length = Math.sqrt(seg.vx * seg.vx + seg.vy * seg.vy);
				unitX = seg.vy / length;
				unitY = -seg.vx / length;
				
				seg.left.x = seg.x + unitX * radius;
				seg.left.y = seg.y + unitY * radius;
				seg.right.x = seg.x - unitX * radius;
				seg.right.y = seg.y - unitY * radius;
			}
			seg = _segments[_segments.length - 1];
			seg.x += seg.vx;
			seg.y += seg.vy;
		}
		
		/**
		 * The number of segments of the snake structure.
		 */
		public function get numSegments():uint { return _numSegments; }
		public function set numSegments(value:uint):void {
			if (value == 0) throw new IllegalOperationError("The number of segments must be at least 2.");
			_numSegments = value;
			_segments = new Array();
			for (var i:int = 0; i < _numSegments - 1; i++) {
				_segments.push( { x:0, y:0 , ox:0, oy:0, vx:0, vy:0, left: { x:0, y:0 }, right: { x:0, y:0 }} );
			}
			_segments.push( { x:0, y:0, vx:0, vy:0 } );
		}
		
		/**
		 * The damping coefficient of the snake strucutre.
		 */
		public function get damping():Number { return _damping; }
		public function set damping(value:Number):void {
			_damping = CJMath.clamp(value, 0, 1);
		}
		
	}
}